#include "winpainter.h"

WinPainterImpl::WinPainterImpl()
    : font_style_( core::FONT_STYLE_NORMAL )
{
    Gdiplus::GdiplusStartupInput gdi_plus_startup_input;
    Gdiplus::GdiplusStartup( &gdi_token_, &gdi_plus_startup_input, NULL );
}

WinPainterImpl::~WinPainterImpl()
{
    Gdiplus::GdiplusShutdown( gdi_token_ );
}

int WinPainterImpl::SetClipRect
              (
                const core::rect &rect
               )
{
    if( (HDC)canvas_ )
    {
        HRGN clip_rect = ::CreateRectRgn( rect.left, rect.top, rect.right, rect.bottom );
        if( clip_rect )
        {
            int clipSave = ::SaveDC( (HDC)canvas_ );
            ::ExtSelectClipRgn( (HDC)canvas_, clip_rect, RGN_AND );
            ::DeleteObject( clip_rect );
            return clipSave;
        }
    }

    return 0;
}

bool WinPainterImpl::ClearClipRect( int clip )
{
    if( !(HDC)canvas_ )
    {
        return false;
    }

    ::RestoreDC( (HDC)canvas_, clip );

    return true;
}

void WinPainterImpl::DrawRectangle
              (
                const core::rect &rect,
                core::dword style,
                core::color color,
                int alpha
               )
{
    WinImage image;
    image.CreateDIBSection( rect.width(), rect.height() );
    {
        common::WinMemoryDC MemDC( image );
        RECT rect_draw = { 0, 0, rect.width(), rect.height() };
        DWORD format = 0;
        if( (style & core::BORDER_STYLE_SOLID) == core::BORDER_STYLE_SOLID )
            format |= PS_SOLID;
        else if( (style & core::BORDER_STYLE_DOTTED) == core::BORDER_STYLE_DOTTED )
            format |= PS_DOT;
        else if( (style & core::BORDER_STYLE_DASHED) == core::BORDER_STYLE_DASHED )
            format |= PS_DASH;

        HPEN hPen = ::CreatePen( format, 1, RGB( color.rgbVal.r, color.rgbVal.g, color.rgbVal.b ) );
        HPEN hOld = (HPEN)::SelectObject( MemDC, hPen );
        ::Rectangle( MemDC, 0, 0, rect.width(), rect.height() );
        ::SelectObject( MemDC, hOld );
        ::DeleteObject( hPen );
    }
    image.SetAlpha( MAX_ALPHA, RGB( color.rgbVal.b, color.rgbVal.g, color.rgbVal.r ) );

    RECT rect_draw = { rect.left, rect.top, rect.right, rect.bottom };
    image.TileBlt( (HDC)canvas_, rect_draw, alpha );
    image.DeleteObject();
}

void WinPainterImpl::FillRectangle
              (
                const core::rect &rect,
                core::color color,
                int alpha
               )
{
    WinImage image;
    image.CreateDIBSection( rect.width(), rect.height() );
    {
        common::WinMemoryDC mem_dc( image );
        HBRUSH brush = ::CreateSolidBrush( RGB( color.rgbVal.r, color.rgbVal.g, color.rgbVal.b ) );
        RECT rect_draw = { 0, 0, rect.width(), rect.height() };
        ::FillRect( mem_dc, &rect_draw, brush );
        ::DeleteObject( brush );
    }
    image.SetAlpha( MAX_ALPHA, RGB( color.rgbVal.b, color.rgbVal.g, color.rgbVal.r ) );

    RECT rect_draw = { rect.left, rect.top, rect.right, rect.bottom };
    image.TileBlt( (HDC)canvas_, rect_draw, alpha );
    image.DeleteObject();
}

void WinPainterImpl::DrawImage
              (
                const std::wstring &file,
                const core::rect &rect,
                int alpha
               )
{
    WinImage image;
    WinResourceImpl * impl = WindowsFactory::Instance().GetResourceImpl();
    if( impl )
    {
        impl->GetImage( file.c_str(), image );
        RECT rect_dest = { rect.left, rect.top, rect.right, rect.bottom };
        image.TileBlt( (HDC)canvas_, rect_dest, alpha );
    }
}

void WinPainterImpl::DrawGridImage
              (
                const std::wstring &file,
                const core::rect &rect,
                const core::rect &grid,
                int length,
                int index,
                int alpha
               )
{
    WinImage image;
    WinResourceImpl * impl = WindowsFactory::Instance().GetResourceImpl();
    if( impl )
    {
        impl->GetImage( file.c_str(), image );
        RECT rect_dest = { rect.left, rect.top, rect.right, rect.bottom };
        RECT rect_grid = { grid.left, grid.top, grid.right, grid.bottom };
        image.GridBlt( (HDC)canvas_, rect_dest, rect_grid, length, index, alpha );
    }
}

void WinPainterImpl::SetTextFont
              (
                const std::wstring &family,
                const core::unit &size,
                core::dword format
               )
{
    font_family_ = family;
    if( size.pixel )
    {
        font_size_ = size.use;
    }
    else
    {
        font_size_ = DEFAULT_FONT_SIZE + (size.use - DEFAULT_FONT_RATE)/DEFAULT_FONT_RATE;
    }

    font_style_ = format;
}

void WinPainterImpl::DrawString
              (
                const std::wstring &text,
                const core::rect &rect,
                core::color color,
                core::dword align,
                int alpha
               )
{
    if( text.size() > 0 && canvas_ )

    {
        WinImage image;
        image.CreateDIBSection( rect.width(), rect.height() );
        {
            common::WinMemoryDC mem_dc( image );
            ::SetBkMode( mem_dc, TRANSPARENT );
            ::SetTextColor( mem_dc, RGB( color.rgbVal.r, color.rgbVal.g, color.rgbVal.b ) );

            HFONT font = FontHelper::CreateTextFont(
                font_family_.c_str(),
                font_size_,
                font_style_ & core::FONT_STYLE_BOLD,
                font_style_ & core::FONT_STYLE_ITALIC,
                font_style_ & core::FONT_STYLE_UNDERLINE );

            HFONT old_font = (HFONT)::SelectObject( mem_dc, font );

            DWORD format = DT_SINGLELINE;
            if( (align & core::HORIZONTAL_ALIGNMENT_CENTER) == core::HORIZONTAL_ALIGNMENT_CENTER )
                format |= DT_CENTER;
            else if( (align & core::HORIZONTAL_ALIGNMENT_RIGHT) == core::HORIZONTAL_ALIGNMENT_RIGHT )
                format |= DT_RIGHT;
            else
                format |= DT_LEFT;

            if( (align & core::VERTICAL_ALIGNMENT_MIDDLE) == core::VERTICAL_ALIGNMENT_MIDDLE )
                format |= DT_VCENTER;
            else if( (align & core::VERTICAL_ALIGNMENT_BOTTOM) == core::VERTICAL_ALIGNMENT_BOTTOM )
                format |= DT_BOTTOM;
            else
                format |= DT_TOP;

            RECT rect_draw = { 0, 0, rect.width(), rect.height() };
            ::DrawText( mem_dc, text.c_str(), text.size(), &rect_draw, format );
            ::SelectObject( mem_dc, old_font );
            ::DeleteObject( font );
        }
        image.SetAlpha( MAX_ALPHA, RGB( color.rgbVal.b, color.rgbVal.g, color.rgbVal.r ) );

        RECT rect_draw = { rect.left, rect.top, rect.right, rect.bottom };
        image.TileBlt( (HDC)canvas_, rect_draw, alpha );
        image.DeleteObject();
    }
}

int WinPainterImpl::GetCharacterPosition
              (
                const std::wstring &text,
                int &width
               )
{
    if( text.empty() )
    {
        return 0;
    }

    int length = (int)text.size();
    common::WinMemoryDC mem_dc( canvas_ );

    HFONT font = FontHelper::CreateTextFont(
        font_family_.c_str(),
        font_size_,
        font_style_ & core::FONT_STYLE_BOLD,
        font_style_ & core::FONT_STYLE_ITALIC,
        font_style_ & core::FONT_STYLE_UNDERLINE );

    HFONT old_font = (HFONT)::SelectObject( mem_dc, font );

    RECT rect_draw = { 0 };

    int index = 0;
    int last = 0;
    LPCWSTR pText = text.c_str();
    for( int i = 0; i <= length; i++ )
    {
        ::DrawText( mem_dc, pText, i, &rect_draw, DT_SINGLELINE | DT_LEFT | DT_CALCRECT );
        int draw_width = rect_draw.right - rect_draw.left;
        if ( draw_width > (float)width )
        {
            break;
        }
        else
        {
            last = draw_width;
            index = i;
        }
    }

    width = last;

    ::SelectObject( mem_dc, old_font );
    ::DeleteObject( font );

    return index;
}